import sys
import configparser
import time
import udsoncan
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTextEdit, QLineEdit
from PyQt5.QtCore import QThread, pyqtSignal, QMutex
from PyQt5 import uic
import can
from isotp import TransportLayer, Address, AddressingMode, TransportLayerLogic, CanStack
from qtpy import QtWidgets
from udsoncan.client import Client
from udsoncan.connections import PythonIsoTpConnection
from PCANBasic import *  ## PCAN - Basic library import
from qtmodern.styles import dark, light
from udsoncan import DidCodec

ecuXorArray = [0x47, 0x53, 0x4D, 0x4B]
ecuXorArray60 = [0x4B, 0x4D, 0x53, 0x47]


# 自定义十六进制编解码器
class HexCodec(DidCodec):
    def __init__(self, length):
        self.length = length

    def decode(self, data):
        if not data:
            return ""
        return data.hex()  # 返回原始十六进制字符串

    def encode(self, value):
        return bytes.fromhex(value)

    def __len__(self):
        return self.length  # 根据配置动态调整长度

    def identity_decode(self, data):
        return data  # 直接返回原始字节
# 读取ini文件
class ConfigReader:
    def __init__(self, config_file):
        self.config = configparser.ConfigParser()
        try:
            self.config.read(config_file)
            self.request_id = int(self.config.get('CAN', 'RequestID'), 16)  # 请求ID
            self.response_id = int(self.config.get('CAN', 'ResponseID'), 16)  # 响应ID
            self.Bitrate = int(self.config.get('CAN', 'Bitrate'))  # 波特率
            self.dids = self.read_did()  # 新增dids属性

        except Exception as e:
            raise ValueError(f"Error reading config file: {e}")
    def read_did(self):
        try:
            # 初始化一个空字典来存储结果
            result_dict = {}

            # 遍历所有的section
            for section in self.config.sections():
                if section.startswith('0x'):
                    # 获取section中的type和length信息
                    section_type = self.config.get(section, 'type')
                    length = int(self.config.get(section, 'length'))

                    if section_type == 'hex':
                        # 使用自定义的十六进制编解码器
                        # format_str = 'B' * length
                        # # 创建DidCodec对象
                        # # codec =
                        # codec = udsoncan.DidCodec(format_str)
                        length = int(self.config.get(section, 'length'))
                        codec = HexCodec(length)  # 使用自定义编解码器
                    elif section_type == 'ascii':
                        # 创建AsciiCodec对象
                        codec = udsoncan.AsciiCodec(length)

                    elif section_type == 'decimal':
                        # 假设对于十进制我们用固定的格式处理，这里以长度为字节数，按大端序处理
                        format_str = '>' + 'B' * length
                        codec = udsoncan.DidCodec(format_str)
                    elif section_type == ' ':
                        # 假设对于十进制我们用固定的格式处理，这里以长度为字节数，按大端序处理

                        codec = udsoncan.DidCodec()
                    else:
                        continue

                    # 将十六进制字符串转换为整数
                    key = int(section, 16)
                    # 将键值对添加到结果字典中
                    result_dict[key] = codec

            return result_dict
        except Exception as e:
            raise ValueError(f"Error reading config file: {e}")
class MultiDIDWorker(QThread):
    signal = pyqtSignal(str)

    def __init__(self, dir_list, canbus, ReadDid):
        QThread.__init__(self)
        self.dir_list = dir_list
        self.can_bus = canbus
        self.ReadDid = ReadDid

    def run(self):
        try:
            self.signal.emit("Connecting to PCAN for multi DID read...")
            # 遍历配置中的DID
            for did in self.dir_list:
                # 构建请求数据
                self.signal.emit(f"Sent request for DID: 0x{did:04X}")
                self.ReadDid(did)  # 发送请求
                time.sleep(0.1)

        except Exception as e:
            self.signal.emit(f"Error: {e}")
class CAN_Receive(QThread):
    signal = pyqtSignal(str)

    def __init__(self, can_bus, open_pcan_flag):
        QThread.__init__(self)
        self.can_bus = can_bus
        self.open_pcan_flag = open_pcan_flag

    def run(self):
        try:
            while self.open_pcan_flag:
                msg = self.can_bus.recv()
                if msg.data is not None:
                    self.signal.emit("RX:{}".format(msg))
                time.sleep(0.01)
            # 发送数据
        except Exception as e:
            self.signal.emit(f"Error: {e}")
# 新增的BRP校准工作线程类
class BRP_angle_sensor_CalibrationWorker(QThread):
    progress_signal = pyqtSignal(int)  # 发送进度信号
    result_signal = pyqtSignal(str)  # 发送结果信号

    def __init__(self, main_window):
        QThread.__init__(self)
        self.main_window = main_window
        # self.mutex = main_window.mutex  # 使用主窗口的锁

    def run(self):
        try:
            if self.main_window.isCh1Open == True:
                self.main_window.conn.open()  # 打开连接
                if self.main_window.sec_request():
                    data_bytes = (0x00).to_bytes(1, byteorder='big')
                    # with self.mutex:  # 使用锁来保护udsclient的访问
                    re = self.main_window.udsclient.routine_control(0xCC01, 0x01, data=data_bytes)
                    if re.positive:
                        self.main_window.result_textbox.append("开始标定角度传感器")
                        for i in range(15):
                            i += 1
                            time.sleep(3)
                            # with self.mutex:  # 使用锁来保护udsclient的访问
                            self.main_window.udsclient.tester_present()

                        data_bytes = (0x01).to_bytes(1, byteorder='big')
                        # with self.mutex:  # 使用锁来保护udsclient的访问
                        re = self.main_window.udsclient.routine_control(0xCC01, 0x01, data=data_bytes)
                        self.main_window.result_textbox.append("角度传感器标定完成")
                        # self.main_window.udsclient.tester_present()
                        # time.sleep(1)
                        # data_bytes = (0x00).to_bytes(1, byteorder='big')
                        # r = self.main_window.udsclient.routine_control(0xCC02, 0x01, data=data_bytes)
                        # if r.positive:
                        #     for i in range(4):
                        #         i += 1
                        #         time.sleep(3)
                        #         self.main_window.udsclient.tester_present()
                        # # r = self.main_window.udsclient.routine_control(0xCC02, 0x01, data=data_bytes)
                        #
                        # # if r.positive:
                        # self.main_window.result_textbox.append("BRP标定完成")
                        # #     self.main_window.result_textbox.append("标定成功")
                        # else:
                        #     self.main_window.result_textbox.append("hell标定失败")
            else:
                self.main_window.result_textbox.append("Tip: The channel is not open. Please open the channel first!")
        except Exception as e:
            self.main_window.result_textbox.append(f'Failed: {str(e)}')
        finally:
            if self.main_window.conn.is_open():
                self.main_window.conn.close()
# 新增的BRP校准工作线程类
class BRP_hell_sensor_CalibrationWorker(QThread):
    progress_signal = pyqtSignal(int)  # 发送进度信号
    result_signal = pyqtSignal(str)  # 发送结果信号

    def __init__(self, main_window):
        QThread.__init__(self)
        self.main_window = main_window
        self.mutex = main_window.mutex  # 使用主窗口的锁

    def run(self):
        try:
            if self.main_window.isCh1Open == True:
                self.main_window.conn.open()  # 打开连接
                if self.main_window.sec_request(): # 安全访问
                    # hell sensor标定
                    self.main_window.result_textbox.append("hell sensor开始标定")
                    # self.main_window.udsclient.tester_present()
                    data_bytes = (0x00).to_bytes(1, byteorder='big')
                    re = self.main_window.udsclient.routine_control(0xCC02, 0x01, data=data_bytes)
                    for i in range(5):
                        i += 1
                        time.sleep(3.5)

                        self.main_window.udsclient.tester_present()

                    # with self.mutex:  # 使用锁来保护udsclient的访问
                    self.main_window.qmessagebox.setText("please cut the KL15 waiting for 10s,then connect KL15")
                    re = self.main_window.udsclient.routine_control(0xCC02, 0x01, data=data_bytes)
                    if re.positive:
                      self.main_window.result_textbox.append("hell sensor标定成功")
                    else:
                        self.main_window.result_textbox.append("标定失败")
            else:
                self.main_window.result_textbox.append("Tip: The channel is not open. Please open the channel first!")
        except Exception as e:
            self.main_window.result_textbox.append(f'Failed: {str(e)}')
        finally:
            if self.main_window.conn.is_open():
                self.main_window.conn.close()
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.Ui = uic.loadUi("DID reader.ui", self)
        self.rev = None
        self.canbus = None
        self.open_pcan_flag = False
        self.isCh1Open = False  # 新增isCh1Open属性
        self.receive_data_flag = False
        self.extended = False
        self.m_objPCANBasic = PCANBasic()
        self.m_PcanHandle = PCAN_USBBUS1
        self.initUI()
        self.mutex = QMutex()  # 初始化锁
        self.qmessagebox = QtWidgets.QMessageBox()

    # 按钮事件
    def initUI(self):
        # 文本框
        self.input_textbox = self.Ui.lineEdit
        # 修改 self.input_textbox 尺寸
        self.result_textbox = self.Ui.textBrowser
        self.result_textbox.setReadOnly(True)
        # 连接按钮事件
        self.Ui.pushButton.clicked.connect(self.connect_pcan)  # 连接PCAN
        self.Ui.pushButton_2.clicked.connect(self.disconnect_pcan)  # 断开PCAN
        self.Ui.pushButton_3.clicked.connect(self.send_data_tp)  # 发送数据
        self.Ui.pushButton_4.clicked.connect(self.read_multi_dids)  # 读取多个DID
        self.Ui.pushButton_5.clicked.connect(self.read_did)  # 接收数据
        self.Ui.pushButton_6.clicked.connect(self.clear_text)  # 清除文本
        self.Ui.pushButton_7.clicked.connect(self.convert)  # 转换功能
        self.Ui.pushButton_8.clicked.connect(self.sec_process)  # 测试安全访问
        self.Ui.pushButton_9.clicked.connect(self.BRP_calibration_)  # 传感器标定
        self.Ui.pushButton_10.clicked.connect(self.BRP_hell_sensor_calibration_)  # hell sensor标定
        # 连接按钮事件
        self.Ui.setWindowTitle('CAN Read DID Tool')
        self.Ui.setGeometry(300, 300, 800, 600)
        # 设置半透明背景
        self.setWindowOpacity(0.9)

    # 清除文本
    def clear_text(self):
        self.result_textbox.clear()

    # 读取单个DID
    def read_did(self):
        try:
            if self.open_pcan_flag:
                if self.Ui.comboBox.currentText() != '':
                    data = self.Ui.comboBox.currentText()
                    # 去除 0x 前缀
                    if data.startswith('0x'):
                        data = data[2:]
                    try:
                        data_bytes = bytes.fromhex(data)
                        # 将 bytes 对象转换为整数
                        data_int = int.from_bytes(data_bytes, byteorder='big')
                        self.result_textbox.append("TX: {}".format(data_bytes.hex()))
                        self.ReadDid(data_int)
                    except ValueError:
                        self.result_textbox.append("The input is not a valid hexadecimal string.")
                else:
                    self.result_textbox.append("Please select a DID.")
            else:
                self.result_textbox.append("Please connect to PCAN first.")
        except Exception as e:
            self.result_textbox.append(f"Error: {e}")

    # 链接P前奏
    def connect_pcan(self):
        try:
            try:
                self.config = ConfigReader('config.ini')
                self.DID = self.config.dids  # 修正为self.config.dids
                self.Ui.comboBox.clear()
                if self.config.request_id > 0x7FF:
                    self.extended = True
                    print('扩展帧类型')
                    self.result_textbox.append("Extended Frame Type")
                else:
                    self.extended = False
                    print('标准帧类型')
                    self.result_textbox.append("Standard Frame Type")
                for did in self.DID:
                    self.Ui.comboBox.addItem(f"0x{did:04X}")
            except ValueError as e:
                print(f"Error: {e}")
                sys.exit(1)
            self.isotp_params = {
                'stmin': 32,
                'blocksize': 8,
                'wftmax': 0,
                'tx_data_length': 8,
                'tx_padding': 0,
                'rx_flowcontrol_timeout': 1000,
                'rx_consecutive_frame_timeout': 1000,
            }
            if not self.open_pcan_flag:
                self.open_pcan_flag = True
                self.result_textbox.append("Connecting to PCAN...")
                self.canbus = openPCAN(self.config.Bitrate,
                                       self.addrees_extended_frame_judge(self.extended)[0])  # 打开通道1
                self.Connect_action()  # 连接PCAN
                self.result_textbox.append("PCAN connected.")
                # self.Receive_data()  # 打开的时候就接收数据，会导致后面发送数据时一直在接收数据
                self.Ui.pushButton.setEnabled(False)
        except Exception as e:
            self.result_textbox.append(f"Error: {e}")

    # 转换成ASCLL码 或者 十进制数的功能
    def convert(self):
        try:
            if self.Ui.lineEdit_2.text() != '':
                data = self.Ui.comboBox_2.currentText()
                if data == 'HexToAscll':
                    self.result_textbox.append(self.hex_to_ascii(self.Ui.lineEdit_2.text()))
                elif data == 'HexToDecimalism':
                    self.result_textbox.append(str(self.hex_to_decimal(self.Ui.lineEdit_2.text())))
                elif data == 'DecimalismToHex':
                    self.result_textbox.append(str(self.decimal_to_hex(self.Ui.lineEdit_2.text())))
                # 去除 0x 前缀
            else:
                self.result_textbox.append("The input data cannot be empty ")

        except Exception as e:
            self.result_textbox.append(f"Error: {e}")

    # 判断是否是扩展帧
    def addrees_extended_frame_judge(self, extended):
        if extended == True:
            return True, AddressingMode.Normal_29bits
        else:
            return False, AddressingMode.Normal_11bits

    # 链接PCAN
    def Connect_action(self):
        try:
            dir = self.config.read_did()  # 读取DID
            # 开启协议栈

            self._isotpaddr_PHYS = Address(self.addrees_extended_frame_judge(self.extended)[1],
                                           txid=self.config.request_id,
                                           rxid=self.config.response_id)  # 网络层寻址方法
            self.tp_stack = CanStack(bus=self.canbus, address=self._isotpaddr_PHYS,
                                     params=self.isotp_params)  # 网络/传输层（IsoTP 协议）
            self.conn = PythonIsoTpConnection(self.tp_stack)  # 应用层和传输层之间建立连接
            self.config = dict(udsoncan.configs.default_client_config)
            self.config['security_algo'] = self.SecAlgo  # 安全算法
            self.config['security_algo_params'] = dict(INITIAL_REMINDER=0xFFFE)
            self.config['data_identifiers'] = dir  # DID数据标识符
            self.config['input_output'] = {0x3B90: udsoncan.DidCodec('B'), }
            self.config['server_address_format'] = 32
            self.config['server_memorysize_format'] = 32
            self.config['standard_version'] = 2006
            # self.config['use_server_timing'] = True
            self.config['request_timeout'] = 5
            self.config['dtc_snapshot_did_size'] = 0x01

            with Client(self.conn, config=self.config, request_timeout=2) as self.udsclient:
                pass
                # self.conn.open()  # 打开连接 已规划在 read函数中打开连接
                # self.conn.close()

            # Prepares the PCAN-Basic's PCAN-Trace file
            self.isCh1Open = True
            print('Is open的状态是：', self.isCh1Open)
            print("通道1已连接")
            # self.result_textbox.setText("提示：通道1已连接！")
            self.result_textbox.ensureCursorVisible()
            self.result_textbox.moveCursor(self.result_textbox.textCursor().End)

            if self.open_pcan_flag == True:
                self.result_textbox.ensureCursorVisible()
                # self.result_textbox.append("提示：CAN通讯已连接！")
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)

        except Exception as err:
            print('出现问题')
            print(err)

    # 没有使用
    def Receive_data(self):
        try:
            if self.open_pcan_flag == True and self.receive_data_flag == False:
                self.receive_data_flag = True
                self.rev = CAN_Receive(self.canbus, self.open_pcan_flag)
                self.rev.signal.connect(self.update_result_textbox)
                self.rev.start()

            else:
                self.result_textbox.append("提示：通道未打开，请先打开通道！")
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)

        except Exception as e:
            print('接收请求失败')
            self.result_textbox.append(f'读取失败:{str(e)}')

    # 读取DID的操作
    def ReadDid(self, DID):
        if self.isCh1Open == True:
            # DID 打印十六进制数
            print("开始读取DID:", hex(DID))
            if not self.conn.is_open():
                self.conn.open()  # 打开连接
            try:
                self.udsclient.change_session(1)
                resp = self.udsclient.read_data_by_identifier([DID])  # 读取DID
                UDS_str = resp.service_data.values[DID]  # 读取DID的值
                # print('UDS发送请求成功')
                # self.conn.close()

                # 将UDS_str转换为字符串类型
                UDS_str = ''.join(str(x) for x in UDS_str) if isinstance(UDS_str, tuple) else str(UDS_str)
                print('UDS发送请求成功')
                self.conn.close()

                self.result_textbox.append(f"The result of reading DID{hex(DID)} is：")
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.append(UDS_str)
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)
            except Exception as e:
                print('UDS发送请求失败')
                self.result_textbox.append(f'DID reading failed: {str(e)}')

        else:
            self.result_textbox.append("Tip: The channel is not open. Please open the channel first!")
            self.result_textbox.ensureCursorVisible()
            self.result_textbox.moveCursor(self.result_textbox.textCursor().End)

    # 断开PCAN
    def disconnect_pcan(self):
        try:
            if self.open_pcan_flag:
                self.open_pcan_flag = False
                self.isCh1Open = False  # 断开连接时设置isCh1Open为False
                self.result_textbox.append("PCAN disconnected.")
                self.Ui.pushButton.setEnabled(True)

            if self.receive_data_flag == True and self.rev.isRunning() == True:
                self.receive_data_flag = False
                self.rev.terminate()
                self.result_textbox.append("Receive data stopped.")
            self.canbus.shutdown()  # 关闭通道1
            self.result_textbox.append("PCAN closed.")
            self.result_textbox.ensureCursorVisible()
            self.result_textbox.moveCursor(self.result_textbox.textCursor().End)
        except Exception as e:
            self.result_textbox.append(f"Error: {e}")

    # 判断 data的前一个字节是否是0x22
    def judge_input_data(self, data):
        if data[0] == 0x22:
            self.result_textbox.append("TX: {}".format('检测到0x22 读取DID功能码'))
            return 0x22
        elif data[0] == 0x2E:
            self.result_textbox.append("TX: {}".format('检测到0x2E 写DID功能码'))
            return 0x2E
        else:
            self.result_textbox.append("TX: {}".format('检测到未知功能码'))
            return False

    def sec_request(self):
        try:
            if self.isCh1Open == True:
                # self.udsclient.change_session(1)  # 先切换到默认会话，可能需要根据实际情况调整
                time.sleep(0.1)
                # self.udsclient.change_session(3)  # 切换到扩展会话
                time.sleep(0.1)
                self.udsclient.change_session(0x65)  # 切换到编程会话
                time.sleep(0.1)
                response = self.udsclient.unlock_security_access(0x06)
                if response.positive:

                    self.result_textbox.append("安全访问解锁成功！")
                    return True
                else:

                    self.result_textbox.append("安全访问解锁失败！")
                    return False
            else:
                self.result_textbox.append("Tip: The channel is not open. Please open the channel first!")
                return False
        except Exception as e:
            print('UDS发送请求失败')
            self.result_textbox.append(f'Failed: {str(e)}')
            return False


    # 写一个安全访问的函数，先切换对话到扩展会话，再切换成编程会话10 0x65，再进行27会话 27 05请求种子 ，27 06发送种子，打印出来是否解锁成功
    def sec_process(self):

        try:
            if self.isCh1Open == True:
                self.conn.open()  # 打开连接

                if self.sec_request():

                    self.conn.close()
                else:

                    return False
            else:
                self.result_textbox.append("Tip: The channel is not open. Please open the channel first!")
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)
        except Exception as e:
            print('UDS发送请求失败')
            self.result_textbox.append(f'Failed: {str(e)}')

        # self.udsclient.conn.close()

    # 写DID
    def write_did(self, did, data):
        if self.isCh1Open == True:
            # DID 打印十六进制数
            print("开始写入DID:", hex(did))
            if not self.conn.is_open():
                self.conn.open()  # 打开连接
            try:
                self.udsclient.change_session(1) # 切换到默认会话
                self.udsclient.change_session(3)  # 切换到扩展会话
                self.udsclient.change_session(0x65)  # 切换到编程会话
                self.udsclient.unlock_security_access(0x06)
                resp = self.udsclient.write_data_by_identifier(did, data)  # 读取DID
                time.sleep(0.1)
                UDS_str = resp.service_data.values[did]  # 读取DID的值
                # print('UDS发送请求成功')
                # self.conn.close()
                # 将UDS_str转换为字符串类型
                UDS_str = ''.join(str(x) for x in UDS_str) if isinstance(UDS_str, tuple) else str(UDS_str)
                print('UDS发送请求成功')
                self.conn.close()
                self.result_textbox.append(f"The result of writing DID{hex(did)} is：")
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.append(UDS_str)
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)
            except Exception as e:
                print('UDS发送请求失败')
                self.result_textbox.append(f'DID reading failed: {str(e)}')
        else:
            self.result_textbox.append("Tip: The channel is not open. Please open the channel first!")
            self.result_textbox.ensureCursorVisible()
            self.result_textbox.moveCursor(self.result_textbox.textCursor().End)

    # 读取多个DID



    def BRP_calibration_(self):
        try:
            times = 0
            if self.isCh1Open == True:
                self.angle_cal = BRP_angle_sensor_CalibrationWorker(self)
                self.Ui.pushButton_9.setEnabled(False)
                self.angle_cal.start()  # 启动线程
                # self.hell_cal= BRP_hell_sensor_CalibrationWorker(self)
                # self.hell_cal.start()
                self.Ui.pushButton_9.setEnabled(True)
            else:
                self.result_textbox.append("Tip: The channel is not open. Please open the channel first!")
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)



        except Exception as e:
            print('UDS发送请求失败')
            self.result_textbox.append(f'Failed: {str(e)}')

        # self.conn.close()

    def BRP_hell_sensor_calibration_(self):
        try:
            times = 0
            if self.isCh1Open == True:
                self.hell_cal = BRP_hell_sensor_CalibrationWorker(self)
                self.Ui.pushButton_10.setEnabled(False)
                self.hell_cal.start()  # 启动线程
                self.Ui.pushButton_10.setEnabled(True)
            else:
                self.result_textbox.append("Tip: The channel is not open. Please open the channel first!")
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)
        except Exception as e:
            print('UDS发送请求失败')
            self.result_textbox.append(f'Failed: {str(e)}')
        # self.conn.close()

    def decimal_to_hex(self, decimal):
        try:
            Hexlist = []
            if " " in decimal:
                dec_list = decimal.split(" ")

                for decimal in dec_list,2:
                    Hexlist.append(hex(decimal))

            else:
                pass
                # result = bytes.fromhex(decimal).decode('ascii')
            return Hexlist
        except ValueError:
            self.result_textbox.append("输入的十进制数据格式不正确，请检查。")
            return None



    def test(self, data):
        re = self.tp_stack.send(data)
        time.sleep(0.1)
        # 接收响应
        response = self.tp_stack.recv(timeout=5)  # 设置超时时间为2秒
        print(response)
        # 打印响应
        if response:
            self.result_textbox.append(f"RX: {response}")
        else:
            self.result_textbox.append("RX: 未收到响应")

    # 发送按钮 发送文本框内的数据
    def send_data_tp(self):
        try:
            if self.open_pcan_flag:
                input_text = self.input_textbox.text().replace(" ", "")
                # 这里需要将输入的文本转换为CAN消息
                if self.Ui.lineEdit.text():
                    try:
                        data = bytes.fromhex(input_text)
                        self.result_textbox.append("TX: {}".format(data.hex()))
                        # 使用ISO - TP协议发送数据
                        if not self.conn.is_open():
                            self.conn.open()  # 打开连接
                        try:
                            re = self.tp_stack.send(data)
                            time.sleep(0.1)
                            # 接收响应
                            response = self.tp_stack.recv(timeout=5)  # 设置超时时间为2秒
                            print(response)
                            # 打印响应
                            if response:
                                self.result_textbox.append(f"RX: {response.hex()}")
                            else:
                                self.result_textbox.append("RX: No response received")

                        except Exception as e:
                            self.result_textbox.append(f"ISO - TP communication error: {e}")
                        finally:
                            if self.conn.is_open():
                                self.conn.close()  # 关闭连接

                    except ValueError:
                        self.result_textbox.append("The input text is not a valid hexadecimal string!")
                else:
                    self.result_textbox.append("The input text cannot be empty!")

            else:
                self.result_textbox.append("Tip: The channel is not open, please open the channel first")
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)
        except Exception as e:
            self.result_textbox.append(f"Error: {e}")

    # 报文接收函数
    def isotp_rcv(self):
        # We execute the "Read" function of the PCANBasic
        result = self.m_objPCANBasic.Read(self.m_PcanHandle)
        if result[0] == PCAN_ERROR_OK:
            # We show the received message
            RevMsg = TPCANMsg()
            RevMsg.ID = result[1].ID
            RevMsg.DLC = result[1].LEN
            for i in range(8 if (result[1].LEN > 8) else result[1].LEN):
                RevMsg.DATA[i] = result[1].DATA[i]
            RevMsg.MSGTYPE = result[1].MSGTYPE
        else:
            RevMsg = None
        return RevMsg

    # 读取多个DID
    def read_multi_dids(self):
        try:
            if self.open_pcan_flag:
                self.worker = MultiDIDWorker(self.DID, self.canbus, self.ReadDid)
                self.worker.signal.connect(self.update_result_textbox)
                self.worker.start()
            else:
                self.result_textbox.append("Tip: The channel is not open, please open the channel first")
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)
        except Exception as e:
            self.result_textbox.append(f"Error: {e}")

    # 大文本框的更新
    def update_result_textbox(self, message):
        self.result_textbox.append(message)

    # Seed计算方法
    def SecAlgo(self, level, seed):
        print("SecAlgo")
        print("level=", level)
        bSeed = seed
        output_key = self.BRP_seedAndKeeyCaculate(seed)
        print("output_key=", output_key)
        # 将列表转换为字节对象
        output_key = bytes(output_key)
        return output_key




    def set_array(self,key, *args):
        key.extend(args)
        return key

    def BRP_seedAndKeeyCaculate(self, seedKey):
        KEYMASK = 0x212
        kmul_index = [0x00, 0x02, 0x10, 0x12, 0x200, 0x202, 0x210, 0x212]
        kmul = [0x9F, 0x2D, 0x80, 0xF9, 0x3F, 0x7F, 0xB2, 0x0A]
        # seeda是种子的前2个字节，即第3和第4个字节
        seeda = (seedKey[0] << 8) + seedKey[1]
        # seedb是种子的后2个字节，即第5和第6个字节
        seedb = (seedKey[2] << 8) + seedKey[3]

        # 循环查找seeda AND KEYMASK在kmul_index表中的位置
        keymulindex = 0
        for index in range(8):
            if (seeda & KEYMASK) == kmul_index[index]:
                keymulindex = index

        # 密钥的前2个字节
        keya = (((seeda ^ 0xFFFF) * kmul[keymulindex]) // 0x40) & 0xFFFF

        # 循环查找seedb AND KEYMASK在kmul_index表中的位置
        keymulindex = 0
        for index in range(8):
            if (seedb & KEYMASK) == kmul_index[index]:
                keymulindex = index

        # 密钥的后2个字节
        keyb = (((seedb ^ 0xFFFF) * kmul[keymulindex]) // 0x40) & 0xFFFF

        key = []
        key = self.set_array(key, ((keya & 0xFF00) >> 8), (keya & 0xFF), ((keyb & 0xFF00) >> 8), (keyb & 0xFF))

        return key

    # -----
    # 字符串类型的hex 数据，转成 ASCLL,先判断输入数据是否存在空格，不存在空格则直接转换，存在空格则先分割空格，再转换
    def hex_to_ascii(self, hex_data):
        try:
            if " " in hex_data:
                hex_list = hex_data.split(" ")
                ascii_chars = []
                for hex_str in hex_list:
                    ascii_chars.append(bytes.fromhex(hex_str).decode('ascii'))
                result = ''.join(ascii_chars)
            else:
                result = bytes.fromhex(hex_data).decode('ascii')
            return result
        except ValueError:
            self.result_textbox.append("输入的十六进制数据格式不正确，请检查。")
            return None

    # 字符串类型的 hex 数据，转成 十进制
    def hex_to_decimal(self, hex_data):
        try:
            if " " in hex_data:
                hex_list = hex_data.split(" ")
                decimal_numbers = []
                for hex_str in hex_list:
                    decimal_numbers.append(int(hex_str, 16))
                return decimal_numbers
            else:
                return int(hex_data, 16)
        except ValueError:
            self.result_textbox.append("输入的十六进制数据格式有误，请检查。")
            return None

    def read_s19_to_flash(self, file_path):
        try:
            if self.isCh1Open == True:
                self.worker = ReadS19ToFlashWorker(file_path, self)
                self.worker.signal.connect(self.update_result_textbox)
                self.worker.start()
            else:
                self.result_textbox.append("Tip: The channel is not open, please open the channel first")
                self.result_textbox.ensureCursorVisible()
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)
        except Exception as e:
            self.result_textbox.append(f"Error: {e}")

    # 读取bin文件,和ini文件中的地址和数据，写入flash
    def read_bin_to_flash(self, file_path):
        try:
            if self.isCh1Open == True:
                self.worker = ReadBinToFlashWorker(file_path, self)
                self.worker.signal.connect(self.update_result_textbox)
                self.worker.start()
            else:
                self.result_textbox.append("Tip: The channel is not open, please open the channel first")
                self.result_textbox.eraseCursor()
                self.result_textbox.moveCursor(self.result_textbox.textCursor().End)
        except Exception as e:
            self.result_textbox.append(f"Error: {e}")
class ReadS19ToFlashWorker(QThread):
    progress_signal = pyqtSignal(int)  # 发送进度百分比
    result_signal = pyqtSignal(str)    # 发送结果信息

    def __init__(self, file_path, main_window):
        QThread.__init__(self)
        self.main_window = main_window
        self.file_path = file_path
        self.mutex = main_window.mutex  # 使用主线程的互斥锁
        self.udsclient = main_window.udsclient  # 引用主线程的 UDS 客户端
        self.conn = main_window.conn        # 引用连接对象

    def run(self):
        try:
            if not self.main_window.isCh1Open:
                self.result_signal.emit("Tip: The channel is not open. Please open the channel first!")
                return

            # 解析 S19 文件
            s19_data = self.parse_s19_file(self.file_path)
            if not s19_data:
                self.result_signal.emit("Error: Invalid S19 file format")
                return

            # 安全访问解锁
            if not self.main_window.sec_request():
                self.result_signal.emit("Security access failed")
                return

            # 切换到扩展会话（按需调整）
            self.main_window.udsclient.change_session(0x03)  # 扩展会话
            time.sleep(0.1)

            # 擦除目标内存区域（示例：假设擦除命令为 0x3101）
            self.send_uds_command("Erase Memory", self.udsclient.routine_control, 0x3101, data=b'\x01')

            # 逐块写入数据
            total_blocks = len(s19_data)
            for block_idx, (address, data) in enumerate(s19_data, 1):
                if not self.write_memory_block(address, data):
                    self.result_signal.emit(f"Write failed at block {block_idx}")
                    return
                self.progress_signal.emit(int((block_idx / total_blocks) * 100))  # 发送进度
                time.sleep(0.05)  # 按需调整延迟

            # 验证写入数据（示例：按需实现）
            self.send_uds_command("Verify Data", self.udsclient.routine_control, 0x3102, data=b'\x01')

            self.result_signal.emit("Flash writing completed successfully!")

        except Exception as e:
            self.result_signal.emit(f"Flash writing failed: {str(e)}")
        finally:
            # 清理操作
            self.main_window.conn.close()
            self.main_window.udsclient.change_session(0x01)  # 切回默认会话

    def parse_s19_file(self, file_path):
        """解析 S19 文件，返回 (address, data) 列表"""
        s19_data = []
        try:
            with open(file_path, 'r') as f:
                for line in f:
                    line = line.strip().upper()
                    if not line.startswith('S'):
                        continue
                    record_type = line[1]
                    if record_type in ('1', '2', '3'):
                        # 解析地址和数据
                        byte_count = int(line[2:4], 16)
                        address = int(line[4:4+2*2], 16) if record_type == '1' else int(line[4:4+3*2], 16)
                        data_hex = line[4+2*2:4+2*2+2*(byte_count-3)]  # 扣除地址和校验和字节
                        data = bytes.fromhex(data_hex)
                        s19_data.append((address, data))
            return s19_data
        except Exception as e:
            print(f"S19解析错误: {e}")
            return None

    def write_memory_block(self, address, data):
        """通过 UDS 写入内存块（示例：使用 0x3D 服务）"""
        try:
            with self.mutex:  # 加锁保护 UDS 操作
                self.udsclient.write_memory_by_address(
                    address_mode=0x04,  # 32位地址（按需调整）
                    memory_address=address,
                    data=data
                )
            return True
        except Exception as e:
            print(f"写入错误: {e}")
            return False

    def send_uds_command(self, message, command_func, *args, **kwargs):
        """发送 UDS 命令并处理响应"""
        try:
            with self.mutex:
                response = command_func(*args, **kwargs)
                if response.positive:
                    self.main_window.result_textbox.append(f"{message}: Success")
                else:
                    self.main_window.result_textbox.append(f"{message}: Failed (Negative Response)")
        except Exception as e:
            self.main_window.result_textbox.append(f"{message}: Error - {str(e)}")
class ReadBinToFlashWorker(QThread):
    progress_signal = pyqtSignal(int)  # 发送进度百分比
    result_signal = pyqtSignal(str)    # 发送结果信息

    def __init__(self, file_path, main_window):
        QThread.__init__(self)
        self.main_window = main_window
        self.file_path = file_path
        self.mutex = main_window.mutex  # 使用主线程的互斥锁
        self.udsclient = main_window.udsclient  # 引用主线程的 UDS 客户端
        self.conn = main_window.conn        # 引用连接对象

    def run(self):
        try:
            if not self.main_window.isCh1Open:
                self.result_signal.emit("Tip: The channel is not open. Please open the channel first!")
                return

            # 读取 bin 文件
            bin_data = self.read_bin_file(self.file_path)
            if bin_data is None:
                self.result_signal.emit("Error: Failed to read the bin file.")
                return

            # 安全访问解锁
            if not self.main_window.sec_request():
                self.result_signal.emit("Security access failed")
                return

            # 切换到扩展会话（按需调整）
            self.main_window.udsclient.change_session(0x03)  # 扩展会话
            time.sleep(0.1)

            # 擦除目标内存区域（示例：假设擦除命令为 0x3101）
            self.send_uds_command("Erase Memory", self.udsclient.routine_control, 0x3101, data=b'\x01')

            # 假设起始地址
            start_address = 0x00000000
            block_size = 256  # 每次写入的数据块大小
            total_blocks = len(bin_data) // block_size + (1 if len(bin_data) % block_size != 0 else 0)

            for block_idx in range(total_blocks):
                start = block_idx * block_size
                end = start + block_size
                block_data = bin_data[start:end]
                address = start_address + start

                if not self.write_memory_block(address, block_data):
                    self.result_signal.emit(f"Write failed at block {block_idx}")
                    return
                self.progress_signal.emit(int((block_idx + 1) / total_blocks * 100))  # 发送进度
                time.sleep(0.05)  # 按需调整延迟

            # 验证写入数据（示例：按需实现）
            self.send_uds_command("Verify Data", self.udsclient.routine_control, 0x3102, data=b'\x01')

            self.result_signal.emit("Flash writing completed successfully!")

        except Exception as e:
            self.result_signal.emit(f"Flash writing failed: {str(e)}")
        finally:
            # 清理操作
            self.main_window.conn.close()
            self.main_window.udsclient.change_session(0x01)  # 切回默认会话

    def read_bin_file(self, file_path):
        try:
            with open(file_path, 'rb') as f:
                return f.read()
        except Exception as e:
            print(f"读取 bin 文件错误: {e}")
            return None

    def write_memory_block(self, address, data):
        """通过 UDS 写入内存块（示例：使用 0x3D 服务）"""
        try:
            with self.mutex:  # 加锁保护 UDS 操作
                self.udsclient.write_memory_by_address(
                    address_mode=0x04,  # 32位地址（按需调整）
                    memory_address=address,
                    data=data
                )
            return True
        except Exception as e:
            print(f"写入错误: {e}")
            return False

    def send_uds_command(self, message, command_func, *args, **kwargs):
        """发送 UDS 命令并处理响应"""
        try:
            with self.mutex:
                response = command_func(*args, **kwargs)
                if response.positive:
                    self.main_window.result_textbox.append(f"{message}: Success")
                else:
                    self.main_window.result_textbox.append(f"{message}: Failed (Negative Response)")
        except Exception as e:
            self.main_window.result_textbox.append(f"{message}: Error - {str(e)}")

def openPCAN(bit_rate, extended_id):
    can0 = can.interface.Bus(interface='pcan', channel='PCAN_USBBUS1', bitrate=bit_rate, extended_id=extended_id)
    return can0


if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 白天7点到晚上7点为dark,晚上为 light
    import datetime

    now = datetime.datetime.now()
    if now.hour >= 9 and now.hour < 16:
        dark(app)
    else:
        light(app)

    # dark(app)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())
